package com.oros.db.appengine;
import com.google.appengine.api.datastore.*;
import com.oros.db.*;
//KeyFactory,

import java.util.*;

public class GAEVaadEngine extends AEVaadTable {

	private static final long serialVersionUID = -8863823906628914860L;
	public final static String TimeStampField ="Time_Stamp";

	   private long GetSequentialId(String tblname) throws Exception
	   {
	      Exception res=null;
	      DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
	      for(int i=0;i<300;i++)
	      {
	       Transaction txn = datastore.beginTransaction();
	       try
	       {
	         long id=1;
	         try 
	         {
	           Key key = KeyFactory.createKey("Table_Sequencer_Id", tblname);
	           Entity theId = datastore.get(key);
	           id =((Long)theId.getProperty("id"))+1;
	           theId.setProperty("id",id);
	           datastore.put(theId);
	         }
	        catch (EntityNotFoundException e1) 
	        {
	           Entity newId = new Entity("Table_Sequencer_Id",tblname);
	           newId.setProperty("id",1);
	           datastore.put(newId);
	           //Entity tblList
	        } 
	         txn.commit();
	        return id;
	       }
	       catch (ConcurrentModificationException e)
	       {
	           res=e;
	       }
	       catch(Exception t)
	       {
	            i=2000;
	            res=t;
	       }
	         finally
	         {
	             if (txn.isActive()) { txn.rollback(); } 
	         }
	       }
	      if (res==null) res = new Exception("App Engine throwable error");
	      throw res;
	   }
	   @Override
	   public void vDeleteRowById(String tblname,String tblname_base,long keyId,AETransactionData transdata,boolean finalCommit,String primaryKey) throws Exception
	   {
		      Exception err=null;
		      DatastoreService datastore;
		      Transaction txn;
		  if (transdata!=null)
		  {
			  if (transdata.datastore!=null && transdata.txn!=null) 
				  {
				     datastore = transdata.datastore;
				     txn = transdata.txn;
				  }
			  else
			  {
				  datastore = DatastoreServiceFactory.getDatastoreService();
				  txn = datastore.beginTransaction();
				  transdata.datastore = datastore;
				  transdata.txn = txn;
			  }
		  }
		  else
		  {
			  datastore = DatastoreServiceFactory.getDatastoreService();
	          txn= datastore.beginTransaction();
		  }
	      Key key = KeyFactory.createKey("SimpleTable",tblname);
	       try
	       {
	             Key itemkey = KeyFactory.createKey(key,tblname_base,keyId);
	             datastore.delete(txn,itemkey);
	             if (finalCommit)
	         	{
	         	     txn.commit();
	         	     if (transdata!=null && transdata.txn!=null)
	         	    	 transdata.txn = null;
	         	}
	       }
	       catch(Exception e1)
	       {
	                err=e1;
	                finalCommit=true;
	       }
	       finally
	       {
	    	   try
	    	   {
	             if  (finalCommit && txn.isActive()) {
	               txn.rollback();
	               if (transdata!=null && transdata.txn!=null)
	      	    	 transdata.txn = null;
	             }
	    	   } 
	    	   catch(Exception e2)
	    	   {
	    		   if (err==null) err=e2;
	    	   }
	           if (err!=null)
	        	   {
	        	       throw err;
	        	   }
	       } 
	   }
	   @Override
	   public void InsertUpdateRow(String tblname,String tblname_base,AERawItem item,AETransactionData transdata,boolean finalCommit) throws Exception
	   {
		   DatastoreService datastore;
		   Transaction txn;
		  if (transdata!=null)
		  {
			  if (transdata.datastore!=null && transdata.txn!=null) 
				  {
				     datastore = transdata.datastore;
				     txn = transdata.txn;
				  }
			  else
			  {
				  datastore = DatastoreServiceFactory.getDatastoreService();
				  txn = datastore.beginTransaction();
				  transdata.datastore = datastore;
				  transdata.txn = txn;
			  }
		  }
		  else
		  {
			  datastore = DatastoreServiceFactory.getDatastoreService();
	          txn= datastore.beginTransaction();
		  }
	       Exception err=null;
	       Key key = KeyFactory.createKey("SimpleTable",tblname);
	       try
	       {
	            Entity ite = new Entity(tblname_base,item.getIdKey(),key);
	            item.copyPropertiesToEntity(ite,this);
	            long commitTime = System.currentTimeMillis();
	            ite.setProperty(TimeStampField, commitTime);
	            datastore.put(ite);
	            if (finalCommit)
	            	{
	            	     txn.commit();
	            	     if (transdata!=null && transdata.txn!=null)
	            	    	 transdata.txn = null;
	            	}
	            item.commit(commitTime);
	       }
	       catch(Exception e1)
	       {
	                err=e1;
	                finalCommit=true;
	       }
	       finally
	       {
	    	   try
	    	   {
	             if  (finalCommit && txn.isActive()) {
	               txn.rollback();
	               if (transdata!=null && transdata.txn!=null)
	      	    	 transdata.txn = null;
	             }
	    	   } 
	    	   catch(Exception e2)
	    	   {
	    		   if (err==null) err=e2;
	    	   }
	       } 
	       if (err!=null)
		   {
		       throw err;
		   }
	   }
	   @Override
	   public void FinalTransactionRollback(AETransactionData transdata) throws Exception
	   {
		   Exception err=null;
		   if (transdata==null || transdata.txn==null) return;
		   try	   
		   {
	           if  (transdata.txn.isActive()) {
			       transdata.txn.rollback();	
	           }
		   }
	       catch(Exception e1)
	       {
	                err=e1;
	       }
	       finally
	       {
	           transdata.txn = null;
	       } 
		   if (err!=null) throw err;
	   }
	   @Override
	   public void FinalTransactionCommit(AETransactionData transdata) throws Exception
	   {
		   Exception err=null;
		   if (transdata==null || transdata.txn==null) return;
		   try	   
		   {
			   transdata.txn.commit();		   
		   }
	       catch(Exception e1)
	       {
	                err=e1;
	       }
	       finally
	       {
	    	   try
	    	   {
	               if  (transdata.txn.isActive()) {
	                   transdata.txn.rollback();
	                   if (err==null) throw new Exception("AppEngine Transaction error");
	               }
	    	   } 
	    	   catch(Exception e2)
	    	   {
	    		   if (err==null) err=e2;
	    	   }
	    	   finally
	    	   {
	    		   transdata.txn=null;
	    	   }
	       } 
	       if (err!=null) throw err;
	   }
	   @SuppressWarnings("unchecked")
	   @Override
	public List<Object> SelectRows(String tblname,String tblname_base,int AppEngineRowLimit,
			   int AppEngineRowOffset,ArrayList<AEFilter> aeFilter,
			   ArrayList<AESort> aeSort) throws Exception
	   {
	       DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
	       Key key = KeyFactory.createKey("SimpleTable",tblname);
	       Query q = new Query(tblname_base, key);
	       int n = aeFilter==null?0:aeFilter.size();
	       for(int i=0;i<n;i++)
	       {
	    	   AEFilter filter = aeFilter.get(i);
	    	   com.google.appengine.api.datastore.Query.FilterOperator operator=
	    			   com.google.appengine.api.datastore.Query.FilterOperator.EQUAL ;
	    	   switch(filter.getFilterOperator())
	    	   {
	    	   case EQUAL:
	    		       operator=com.google.appengine.api.datastore.Query.FilterOperator.EQUAL;
	    		       break;
	    	   case GREATER_THAN:
	    		       operator=com.google.appengine.api.datastore.Query.FilterOperator.GREATER_THAN;
	    		       break;	    
	    	   case GREATER_THAN_OR_EQUAL:
		    		   operator=com.google.appengine.api.datastore.Query.FilterOperator.GREATER_THAN_OR_EQUAL;
		    		   break;	    	   
	    	   case IN:
    		           operator=com.google.appengine.api.datastore.Query.FilterOperator.IN;
    		           break;	    
    	       case LESS_THAN:
		               operator=com.google.appengine.api.datastore.Query.FilterOperator.LESS_THAN;
		               break;	    
	           case LESS_THAN_OR_EQUAL:
    		           operator=com.google.appengine.api.datastore.Query.FilterOperator.LESS_THAN_OR_EQUAL;
    		           break;	    	   
		       case NOT_EQUAL:
			    	   operator=com.google.appengine.api.datastore.Query.FilterOperator.NOT_EQUAL;
			    	   break;	    		   
	    	   }
	    	   q.addFilter(filter.getColumnName(),operator,filter.getValue());
	       }
	       n = aeSort==null?0:aeSort.size();
	       for(int i=0;i<n;i++)
	       {
	    	   AESort sorter = aeSort.get(i);
	    	   com.google.appengine.api.datastore.Query.SortDirection direction = com.google.appengine.api.datastore.Query.SortDirection.ASCENDING;
	    	   if (sorter.getSortDirection()==AESort.SortDirection.DESCENDING) 
	    		   direction = com.google.appengine.api.datastore.Query.SortDirection.DESCENDING;
	    	   q.addSort(sorter.getColumnName(),direction);
	       }
	       PreparedQuery pq = datastore.prepare(q);
	       List<Object> data =(List<Object>)(Object) pq.asList(FetchOptions.Builder.withLimit(AppEngineRowLimit).offset(AppEngineRowOffset));
	       return data;
	   }               
	   
	   public List<String> SelectTables(String tblname_start)
	   {
	       DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
	       //Key key = KeyFactory..createKey("SimpleTable",KIND_METADATA_KIND);
	       Query q = new Query("Table_Sequencer_Id");
	       PreparedQuery pq = datastore.prepare(q);
	       List<Entity> data = pq.asList(FetchOptions.Builder.withLimit(10000));
	       int n= data.size();
	       ArrayList<String> dataNew = new ArrayList<String>(n);
	       boolean fr = tblname_start==null || tblname_start.length()==0;
	       for(int i=0;i<n;i++)
	       {
	    	   String s = data.get(i).getKey().getName();
	    	   if (fr)
	    	       dataNew.add(s);
	    	   else if (s.startsWith(tblname_start)) dataNew.add(s); 
	       }
	       return dataNew;
	   }
	   @Override
	   public List<Object> SelectRowsGeneral(String tblname,String tblname_base,int AppEngineRowLimit,
			   int AppEngineRowOffset,ArrayList<AEFilter> aeFilter,
			   ArrayList<AESort> aeSort) throws Exception
	   {
		   List<Object> generalEntity = new ArrayList<Object>();
		   List<String> tableList = SelectTables(tblname);
		   Collections.sort(tableList);
		   int n=tableList.size();
		   for(int i=0;i<n;i++)
		   {
			   String realTblName = tableList.get(i);
			   List<Object> tmpList = SelectRows(realTblName,tblname_base,AppEngineRowLimit,AppEngineRowOffset,
					   aeFilter,aeSort);
			   int k = tmpList.size();
			   for(int j=0;j<k;j++)
			   {
				   generalEntity.add(tmpList.get(j));
			   }
		   }
	       return generalEntity;
	   }

	@Override
	public boolean vdIsTimeStampSupported() {
		return true;
	}

	@Override
	public Object vdGetTimeStamp(Object entity) {
		return ((Entity)entity).getProperty(TimeStampField);
	}

	@Override
	public Object vdGetFieldValue(Object entity, String fieldName, int fieldNo) {
		return ((Entity)entity).getProperty(fieldName);
	}

	@Override
	public void vdSetFieldValue(Object entity, String fieldName, int fieldNo,
			Object value) throws Exception {
		((Entity)entity).setProperty(fieldName, value);
	}

	@Override
	public long vvdGetPrimaryId(Object entity,String idColumnName,int idColumnNr) {
		return  ((Entity)entity).getKey().getId();
	}
	@Override
	public void allocateNewId(AERawItem item, String tableName) throws Exception {
		long id = GetSequentialId(tableName);
		item.setPrimaryKey(id);
	}
	@Override
	public boolean isNewItemHasDummyId() {
		return false;
	}
	@Override
	public boolean isCommitNeededRefresh() {
		return false;
	} 
	 @Override
	public boolean isBaseTableSeparationSupported()
	{
		return true;
	}
	@Override 
	public boolean isAEFilterAndSorterSupported()
	{
		return true;
	}

}
